package org.streets.commons.structure;

import java.net.URL;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;
import org.xml.sax.InputSource;

import org.streets.commons.util.XMLUtils;

/**
 *
 * @author Zenberg.D
 */
public final class MimeType {

	private static final Log log = LogFactory.getLog(MimeType.class.getName());
    private static Map<String, MimeType> mtypes = new HashMap<String, MimeType>();


	private String name = "";
	private Set<String> exts = new HashSet<String>();
	private Set<Magic> magics = new HashSet<Magic>();

    static {
        initialize("MimeType.xml");
    }

    private static void initialize(String fname) {
        try {
            Document document = XMLUtils.parse(new InputSource(MimeType.class.getResourceAsStream(fname)), null);
            NodeList nodes = document.getDocumentElement().getElementsByTagName("mime-type");
            for (int i=0; i<nodes.getLength(); i++) {
                Element elem = (Element) nodes.item(i);
                MimeType mt = new MimeType(elem);
                mtypes.put(mt.getName(), mt);
            }
        } catch (Exception ex) {
            log.error(ex.getMessage(), ex);
        }
    }

    private MimeType(Element elem) {
        this.name = elem.getAttribute("name");
        NodeList extList;
        extList = elem.getElementsByTagName("ext");
        for (int i=0; i<extList.getLength(); i++) {
        	this.exts.add(extList.item(i).getNodeValue());
        }
        //<magic offset="0" type="byte" value="3c4d494646696c6520"/>
        extList = elem.getElementsByTagName("magic");
        for (int i=0; i<extList.getLength(); i++) {
            String offset = ((Element)extList.item(i)).getAttribute("offset");
            String type = ((Element)extList.item(i)).getAttribute("type");
            String value = ((Element)extList.item(i)).getAttribute("value");
            if (offset != null) {
            	try {
            		this.magics.add(new Magic(Integer.parseInt(offset), type, value));
            	} catch(Exception e) {
            		System.err.println("Magic Offset Error :"+offset+"\n  Mime name is: "+name);
            	}
            }
        }
    }

    public String getName() {
    	return name;
    }

    public Set<String> getExtNames() {
    	return exts;
    }

    public Set<Magic> getMagics() {
    	return magics;
    }

    public String toString() {
    	return name;
    }

    public static MimeType getMimeType(String mimeName) {
        return mtypes.get(mimeName);
    }

    public static MimeType getMimeType(URL url) {
    	return null;
    }

    public static MimeType getMimeTypeByExtName(String extName) {
    	return null;
    }

    class Magic {

        int offset = -1;
        byte[] value = null;

        Magic(int offset, String type, String value) {
            this.offset = offset;
            if ("byte".equals(type)) {
                this.value = this.readBytes(value);
            } else {
                this.value = value.getBytes();
            }
        }

        int size() {
            return (offset + value.length);
        }

        boolean matches(byte[] data) {
            if (data == null) { return false; }

            int idx = offset;
            if ((idx + value.length) > data.length) {
                return false;
            }

            for (int i=0; i<value.length; i++) {
                if (value[i] != data[idx++]) {
                    return false;
                }
            }
            return true;
        }

        byte[] readBytes(String magic) {

            if (magic.length() % 2  != 0) return null;

            String tmp = magic.toLowerCase();
            byte[] data = new byte[tmp.length() / 2];
            int byteValue = 0;

            for (int i=0; i<tmp.length(); i++) {
                char c = tmp.charAt(i);
                int number;
                if (c >= '0' && c <= '9') {
                    number = c - '0';
                }
                else if (c >= 'a' && c <= 'f') {
                    number = 10 + c - 'a';
                }
                else {
                    throw new IllegalArgumentException();
                }
                if ((i % 2) == 0) {
                    byteValue = number * 16;
                }
                else {
                    byteValue += number;
                    data[i/2] = (byte) byteValue;
                }
            }
            return data;
        }
    }
}
